feat(deploy): add native support for Netlify, Render, and Railway deployments - #3388
feat(deploy): add native support for Netlify, Render, and Railway deployments#3388Maou3434 wants to merge 28 commits into
Conversation
Siri-Ray
left a comment
There was a problem hiding this comment.
@Maou3434 Thanks for pushing this deployment expansion. I reviewed the daemon provider flows, web provider selection/config plumbing, contracts, and the added tests. I found a blocking data-loss issue in the Netlify/Render deploy paths where deploying can overwrite user-authored provider config files in the project directory; I also left a smaller GitHub sync path fix. Once the config-file writes are made upload-only and existing project files are preserved, this should be much safer to ship.
π Powered by Looper Β· runner=reviewer Β· agent=codex Β· An autonomous AI dev team for your GitHub repos.
Siri-Ray
left a comment
There was a problem hiding this comment.
@Maou3434 Thanks for the follow-up here. The prior local config overwrite and GitHub path encoding issues look addressed on this head. I found one remaining blocking i18n contract issue: the typed dictionary now requires Render/Railway/GitHub PAT copy, but most locale files do not define those keys yet, so the web typecheck will fail once dependencies are present.
π Powered by Looper Β· runner=reviewer Β· agent=codex Β· An autonomous AI dev team for your GitHub repos.
Siri-Ray
left a comment
There was a problem hiding this comment.
@Maou3434 Thanks for the continued work on these deployment providers. I reviewed the current Netlify/Render/Railway deploy plumbing, provider config handling, UI provider selection, i18n keys, and the added deploy tests. The earlier config overwrite and typed-locale blockers look addressed, but I found one remaining Netlify primary-flow issue where a failed repo-link step can still be reported as a successful deploy.
π Powered by Looper Β· runner=reviewer Β· agent=codex Β· An autonomous AI dev team for your GitHub repos.| }, | ||
| }), | ||
| }); | ||
| if (!updateSiteResp.ok) { |
There was a problem hiding this comment.
Blocking: this leaves the Netlify site-link failure as a console warning and then continues into the build trigger path. The changed flow is supposed to fix Netlify's repository access by attaching repo_id/deploy_key_id, but if this PUT /sites/:siteId is rejected for an existing site, the site remains linked to the old repository settings or unlinked entirely. The later /builds call can then fail or pick up an older deploy from the deploys-list fallback, while the route still persists a URL/status for the current OD deploy. Please make the repo-link update a required step: parse the Netlify error body and throw a DeployError when this update fails, and cover that case in apps/daemon/tests/deploy.test.ts so we do not mark stale or unlinked Netlify builds as successful.
There was a problem hiding this comment.
I've addressed the issue, and the fix is fairly small and self-contained. If you have a moment, I'd really appreciate it if you could review the PR and help move it forward. Thank you!
There was a problem hiding this comment.
Hey @Maou3434 β thanks for following up. The remaining thread we are tracking here is @Siri-Rays Netlify repo-link failure blocker, and GitHub is currently marking this head as DIRTY, so it is worth rebasing on main as well if you have not already. @elihahah666 is already on the reviewer list for the feature-side pass; once that blocking thread is confirmed on this head, the next step is a fresh human re-review.
There was a problem hiding this comment.
Thanks for the update. I've now rebased onto main and pushed the latest changes. The Netlify repo-link issue should be addressed on this head, so it should be ready for a fresh review when convenient. Thank you!!
There was a problem hiding this comment.
Hey @Maou3434 β thanks for rebasing and pushing the follow-up. The current head is back to green on CI, and this branch now has the Netlify repo-link failure path throwing on both the existing-site and fallback-site update paths with matching daemon test coverage. The remaining step is a fresh pass from @Siri-Ray on this still-open thread, since that older blocking review is what GitHub is still carrying on the PR.
c8941e9 to
9a37680
Compare
9a37680 to
06cc2f9
Compare
|
Hey @Maou3434 β thanks for picking this up and for tying it back to the broader deploy-button push. The direction is exciting here: adding native Netlify / Render / Railway paths plus the provider-specific credential flow is a meaningful user-facing surface. Iβve done the missed opened-path triage on our side: the PR is already classified, the related request is tracked in #1027, and @Siri-Ray + @elihahah666 are on the review path. Iβm staying out of code-review findings in this pass because bot code review is currently paused, but the routing and product-review bookkeeping are now caught up. |
|
Thanks for the update and for handling the triage and review routing. I'll keep an eye out for any feedback from the reviewers. |
84bba89 to
a529b1e
Compare
|
Hey @Siri-Ray β gentle bump on this one. @Maou3434 rebased again on 2026-06-06 (head |
|
Hey @Siri-Ray β just following up on this again when you have a chance. The implementation has been up for about a week now, and I've rebased a few times to keep the branch current. The previous Netlify repo-link concerns should be addressed on the current head, CI is green, and @lefarcen already noted that the earlier review is now stale relative to the latest changes. I'd really appreciate a fresh pass whenever you're able. Also, thanks @lefarcen for keeping an eye on the PR and helping move it along. Hopefully we can get this over the line before rebase number four π |
|
Hey @Maou3434 β quick heads-up: this PR now has The main actionable review on this head is @elihahah666's current π‘ To drive this PR to merge hands-free, paste this to your AI coding agent (Claude Code / Codex / opencode / Cursor β¦): |
|
Hey @Maou3434 β thanks for adding the copy-link capability here. A few product-direction follow-ups before we can move this forward:
Let us know if you'd like any pointers on the updated share/download pattern or UI integration direction. π |
a529b1e to
3e754a6
Compare
|
Hey @elihahah666, I've addressed all of the review comments from the latest pass, including the blocking Railway GraphQL error-handling issue and the remaining robustness/convention items. The deployment flows, provider plumbing, tests, routes, contracts, and web integration should now be in good shape. Apart from the UI concerns expressed above it should be good to go |
Hey @lefarcen
If this looks good and aligns with the intended UX, I'll go ahead and push it. |
This commit introduces full support for deploying projects directly to Railway as static sites. Similar to Render, Railway static sites are backed by a Git provider reference, so this implementation automatically synchronizes the project files to a public GitHub repository and triggers a deployment on Railway. Backend (apps/daemon & contracts): - Implemented deployToRailway using the GitHub REST API to check/create public repositories named od-\ and sync file contents, and the Railway GraphQL API (v2) to programmatically orchestrate project, environment, and service provisioning. - Injected a Staticfile configuration dynamically into the synchronized file set so that Railway (Nixpacks/Railpack) correctly detects and hosts the files as a static site with index fallback. - Updated deploy routes and server bindings to recognize and route RAILWAY_PROVIDER_ID requests. - Added apps/daemon/test_deploy_trigger.cjs to easily trigger and debug the integration manually. - Added comprehensive unit testing for deployToRailway inside apps/daemon/tests/deploy.test.ts. Frontend (apps/web): - Updated FileViewer.tsx to handle dual-token configurations, presenting both the Railway API Token and GitHub PAT fields dynamically with proper placeholder masking. - Added Railway support to the provider registry (registry.ts), validation tests, and analytical event tracking. - Added localization strings and types for Railway deployment settings across en and zh-CN locales.
- Retrieve and track the GitHub repository ID during creation or check - Generate Netlify deploy keys and register public keys to the synced GitHub repository - Supply `repo_id` and `deploy_key_id` to Netlify site creation, update, and build API requests - Add test coverage and mock endpoints in `deploy.test.ts`
Addresses multiple edge cases, potential race conditions, and synchronization
performance across Vercel, Railway, Render, Netlify, and GitHub deployment paths:
- Vercel: Automatically resolves `teamId` from `teamSlug` via `GET /v2/teams`
to correctly scope deployment status queries.
- Railway: Adds an `isNewService` check on service creation to skip duplicate
manual trigger builds. Propagates unified `DeployError` on V1+V2 mutation failures.
- Render: Introduces a robust polling mechanism (`pollRenderDeploy`) using
`GET /services/{serviceId}/deploys/{deployId}` to catch and propagate build failures.
- Netlify: Avoids socket hangups on trigger builds using a 15-second abort signal
timeout. Restricts `/deploys` fallback query to successful trigger builds, and
carries repo visibility (`isPrivate`) into the link settings.
- GitHub: Extracts and unifies repository verification/creation into a common
`ensureGitHubRepository` helper which handles 422 concurrent creation race conditions.
- Git Blob SHA Optimization: Implements local Git Blob SHA calculation (`gitBlobSha`)
and updates `createOrUpdateGitHubFile` to query remote file SHAs, skipping the
PUT request if the file content matches.
Added/updated regression tests in deploy.test.ts to verify the new behaviors.
β¦ty conflicts
This fixes two critical deploy flow issues:
1. Netlify deploy key accumulation:
- Netlify site deploy_key_id is now persisted under providerMetadata.
- On redeployment, the existing key is retrieved from priorMetadata, or fetched from the Netlify site object (checking deploy_key_id, repo.deploy_key_id, and build_settings.deploy_key_id).
- A new deploy key is only created if none was resolved.
- Added a regression test verifying keys are not generated on subsequent runs.
2. Cross-provider repo visibility conflicts:
- Namespaced GitHub repositories to be provider-specific ('od-netlify-\', 'od-render-\', and 'od-railway-\').
- This ensures Render and Railway can safely clone their public repositories, even if Netlify has configured its own repository as private.
- Updated all test mock configurations to match the new namespaces.
β¦y and Render This addresses an edge case where a user's default branch on GitHub is not 'main' (e.g. 'master'): 1. Updated ensureGitHubRepository to return the defaultBranch extracted from the GitHub API response, falling back to 'main' if not found. 2. Replaced the hard-coded 'main' branch string in deployToNetlify (creation and update payloads) with defaultBranch. 3. Replaced the hard-coded 'main' branch string in deployToRender (service creation payload) with defaultBranch. 4. Added unit tests for Netlify and Render validating correct propagation of non-main branches (e.g., 'master').
β¦back
This addresses a race condition in the Netlify build trigger fallback:
1. Query and store the latest deploy ID from Netlify before calling POST /sites/{id}/builds.
2. In the fallback path (when the trigger succeeds but omits the deploy_id), poll the deploys list up to 5 times (1s intervals).
3. Verify that the resolved deploy ID differs from the pre-trigger deploy ID to ensure we don't bind to a stale previous run, throwing a DeployError if none is resolved.
4. Suppressed mock-related fetch logs in Vitest run environments.
5. Added a regression test: 'polls and resolves new Netlify deploy when trigger response lacks deploy_id and new deploy is delayed'.
β¦ test mocks
This addresses a social share retry provider mismatch and localizes the daemon test fetch mock wrapper to prevent global Vitest worker pollution.
Thread target provider ID (via latestSocialShareDeployment?.providerId) into retryDeploymentLink inside FileViewer.tsx.
In retryDeploymentLink, search for active deployment targeting the specified provider ID, falling back to the current modal-level deployProviderId.
Removed the top-level global monkeypatch of vi.stubGlobal from deploy.test.ts to keep Vitest environment clean.
Added a local stubGlobalFetch(fetchMock) helper inside deploy.test.ts to wrap fetch mocks and handle unexpected git/trees/ calls locally.
Updated all fetch mocking tests in deploy.test.ts to use stubGlobalFetch instead of vi.stubGlobal('fetch').
This addresses a race condition and error mapping in the Railway and GitHub deploy processes: - Query and store the latest deploy ID from Railway before triggering build. - Poll the deploys list up to 5 times (1s intervals) to resolve the new deploy ID, verifying it differs from the pre-trigger deploy ID. - Poll the resolved Railway deployment until status becomes ACTIVE. - Support specific Railway deployment status queries in check-link endpoint. - Map GitHub upload/PUT failures to 502 Bad Gateway. - Verify that social share auto-opening transition is only executed if the deploy modal was opened from the share flow.
β¦elays This addresses a correctness issue where Render deploy IDs are missing or delayed: - Fetch and record latest deploy ID before triggering build as preTriggerDeployId. - Poll Render deploys list up to 5 times (1s intervals) to resolve the new deploy ID, verifying it differs from preTriggerDeployId. - Persist resolved deploy ID in providerMetadata. - Add regression test: 'polls and resolves new Render deploy when trigger response lacks deploy_id and new deploy fails'.
β¦cess in check-link This addresses correctness and reliability in PaaS deployment flows: Query Netlify using GET /deploy_keys/:id to fetch the public key during deploy key reuse, ensuring it is repaired/registered on GitHub, or rotating the key pair if missing. Add provider-specific check-link handlers checkNetlifyDeploymentLinks and checkRenderDeploymentLinks that query the deployment/build status from the Netlify and Render APIs, falling back to reachability checks once confirmed ready. Wire Netlify and Render check-link handlers into the daemon check-link route and server dependencies. Added unit tests to verify checkNetlifyDeploymentLinks and checkRenderDeploymentLinks behavior across ready, building, and failed states, and updated Netlify key reuse test to assert GitHub registration.
β¦ button in FileViewer
β¦ deploy key Add validation queries at the start of Netlify, Render, and Railway deploys. Stored site, service, and project IDs are treated as hints and cleared if the provider returns a 404 / Not Found error, triggering name-based re-resolution and recreation. Whitelist 422 validation duplicate key errors from GitHub (e.g. "key is already in use") during deploy key registration to allow healthy redeploys to succeed. Add unit tests covering recovery from stale resource IDs and handling of duplicate GitHub deploy keys.
β¦ne deploy ID verification Throw a DeployError on non-OK responses from Netlify and Render site/service list queries to prevent duplicate site/service creation during transient API errors. Enforce baseline pre-trigger deployment ID verification in Netlify and Render. If the pre-trigger deploy check fails, leave the baseline ID as undefined and throw a DeployError if the trigger response omits the build/deploy ID, preventing the fallback from accepting a stale deployment. Add unit tests covering list lookup errors and pre-trigger check failures.
β¦ues and query timeout budget
β¦on and web - Resolved merge conflicts in apps/web/src/components/FileViewer.tsx by correctly shifting deploy actions and copy links from the download dropdown to the share/publish dropdown. - Configured individual provider copy links to only show when there are multiple active deployments (threshold > 1) to eliminate single-item redundancy. - Fixed a bug in deployToRailway where a DeployError was incorrectly thrown even if the fallback mutation succeeded. - Fixed a mock pollution issue in deploy.test.ts GET requests by restricting getHeaders capture to target files. - Added missing PUT config mock and asynchronous state wait race condition handling in FileViewer.test.tsx.
β¦ght worker fixture ownership
c81bc1e to
c2f44a2
Compare
|
Hey a gentle reminder on this PR, only needs final pass from nettee i believe |
|
Hey @Maou3434 β quick state update on the new head
|
|
ππ gentle reminder!! |


















































































Resolves the PaaS deployment feature request for one-click deployment buttons.
Why
What users will see
Surface area
apps/web(FileViewer.tsx)apps/daemon(deploy-routes.ts,server.ts)apps/web/src/i18n/locales/*)Screenshots
Share UI

Netlify

Render

Railway

Bug fix verification
deploy_key_idandrepo_idinside the Netlify site configuration.apps/daemon/tests/deploy.test.tsto verify both first-time deploy key creation and site linkage fallbacks.Validation
pnpm typecheckpnpm --filter @open-design/web testand daemon deployment suite tests